package com.okaixz.manager.utils;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.okaixz.client.model.dto.WxPayAttachInfo;
import com.okaixz.client.properties.WxProperties;
import com.okaixz.common.client.vo.WxMiniProgramPayClientReqeust;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.nativepay.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

/**
 * @author 陈大专
 * @time 2024/10/22 8:56
 * @des 微信支付工具类
 **/
@Component
@Slf4j
public class WxPayUtil {

    @Autowired
    private WxProperties wxProperties;


    /**
     * 商户号
     */
    public static String merchantId = "1695686516";
    //    /**
    //     * 商户API私钥路径
    //     */
    //    public static String privateKeyPath = "C:\\Users\\yxky-077\\Desktop\\natapp_windows_amd64_2_4_0\\apiclient_key.pem";
    //    /**
    //     * 商户证书序列号
    //     */
    //    public static String merchantSerialNumber = "4D15D269427B04A76A0D3004833D380530FC802B";
    //    /**
    //     * 商户API私钥路径
    //     */
    //    public static String privateKeyPath = "C:\\software\\wxcertutil\\WXCertUtil\\cert\\apiclient_key.pem";
    /**
     * 商户证书序列号
     */
    public static String merchantSerialNumber = "400DC5088DB184F7640B673617C41B6068CA0151";
    /**
     * 商户API私钥路径
     */
    public static String privateKeyPath = "/usr/local/okai-server/apiclient_key.pem";
    /**
     * 商户APIV3密钥
     */
    public static String apiV3Key = "eN6wU6jI4yW2kW0kF8fG6vC7oU4mV3cX";

    //    /**
    //     * 支付通知回调地址
    //     */
    //    public static String notify_url = "https://ux328251nc8.vicp.fun/wx/native_pay_callback";
    /**
     * 支付通知回调地址
     */
    public static String notify_url = "https://www.okaixz.com/api/wx/native_pay_callback";

    /**
     * 获取支付二维码链接
     *
     * @param price 价格单位分
     * @return
     */
    public String getPayUrl(String orderNo, String userId, Integer gid, String sessionId, Integer price, String gname) {
        String qrcode = null;
        WxPayAttachInfo attachInfo = new WxPayAttachInfo();
        attachInfo.setA(userId);
        attachInfo.setB(gid);
        attachInfo.setC(sessionId);
        try {
            // 使用自动更新平台证书的RSA配置
            // 一个商户号只能初始化一个配置，否则会因为重复的下载任务报错
            Config config =
                    new RSAAutoCertificateConfig.Builder()
                            .merchantId(merchantId)
                            .privateKeyFromPath(privateKeyPath)
                            .merchantSerialNumber(merchantSerialNumber)
                            .apiV3Key(apiV3Key)
                            .build();
            // 构建service
            NativePayService service = new NativePayService.Builder().config(config).build();
            // request.setXxx(val)设置所需参数，具体参数可见Request定义
            PrepayRequest request = new PrepayRequest();
            Amount amount = new Amount();
            //            amount.setTotal(1);
            amount.setTotal(price);

            request.setAmount(amount);
            request.setAppid(wxProperties.getAppid());
            request.setMchid(merchantId);
            request.setDescription(gname);
            request.setNotifyUrl(notify_url);
            //            request.setAttach(userId+gid);
            request.setAttach(JSON.toJSONString(attachInfo));
            request.setOutTradeNo(orderNo);
            // 调用下单方法，得到应答
            PrepayResponse response = service.prepay(request);


            // 使用微信扫描 code_url 对应的二维码，即可体验Native支付
            qrcode = response.getCodeUrl();
            System.out.println(qrcode);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return qrcode;
    }

    private CloseableHttpClient httpClient;
    private PrivateKey privateKey;

    public WxMiniProgramPayClientReqeust getMinProgramPayRequest(String sessionId, String userId, String openId, String orderNo, Integer gid, Integer price, String gname) throws Exception {
        WxPayAttachInfo attachInfo = new WxPayAttachInfo();
        attachInfo.setA(userId);
        attachInfo.setB(gid);
        attachInfo.setC(sessionId);


        if (httpClient == null) {
            privateKey = readPrivateKey(privateKeyPath);
            // 加载平台证书（mchId：商户号,mchSerialNo：商户证书序列号,apiV3Key：V3密钥）
            // 获取证书管理器实例
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
                    new PrivateKeySigner(merchantSerialNumber, privateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
            // ... 若有多个商户号，可继续调用putMerchant添加商户信息

            // 从证书管理器中获取verifier
            Verifier verifier = certificatesManager.getVerifier(merchantId);
            WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                    .withMerchant(merchantId, merchantSerialNumber, privateKey)
                    .withValidator(new WechatPay2Validator(verifier));

            // 初始化httpClient
            httpClient = builder.build();
        }

        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", merchantId)
                .put("appid", wxProperties.getMinAppid())
                .put("description", gname)
                .put("notify_url", notify_url)
                .put("out_trade_no", orderNo)
                .put("attach", JSON.toJSONString(attachInfo));
        rootNode.putObject("amount")
                .put("total", price);
        rootNode.putObject("payer")
                .put("openid", openId);

        objectMapper.writeValue(bos, rootNode);

        String jsonString = JSON.toJSONString(rootNode);
        //        httpPost.addHeader("Authorization", getPayAuth(jsonString));

        httpPost.setEntity(new StringEntity(jsonString, "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        JSONObject obj = JSON.parseObject(bodyAsString);
        String prepay_id = obj.getString("prepay_id");

        WxMiniProgramPayClientReqeust reqeust = getWxMiniProgramPayClientReqeust(prepay_id);

        String result = JSON.toJSONString(reqeust);
        System.out.println(result);
        return reqeust;
    }


    /**
     * 解析支付回调
     *
     * @param request
     * @param response
     * @return
     */
    public WxPayResult processResult(HttpServletRequest request, HttpServletResponse response) {
        Transaction transaction = null;
        WxPayResult result = new WxPayResult();
        JSONObject jsonObject = new JSONObject();
        try {
            // 获取请求体原内容（此时获取的数据是加密的）
            BufferedReader reader = request.getReader();
            StringBuilder requestBody = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
            String reqeustJson = requestBody.toString();
            System.out.println("微信支付回调：" + reqeustJson);

            // 获取请求携带的数据，构造参数
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(request.getHeader("Wechatpay-Serial")) // 微信支付平台证书的序列号
                    .nonce(request.getHeader("Wechatpay-Nonce")) // 签名中的随机数
                    .signature(request.getHeader("Wechatpay-Signature"))  // 应答的微信支付签名
                    .timestamp(request.getHeader("Wechatpay-Timestamp")) // 签名中的时间戳
                    .body(reqeustJson) // 请求体内容（原始内容，不要解析）
                    .build();

            // 初始化RSAAutoCertificateConfig
            NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(merchantId) // 商户号
                    // API证书路径，路径自己调试，能找到就行（NativeUtil为自定义工具类）
                    .privateKeyFromPath(privateKeyPath)
                    .merchantSerialNumber(merchantSerialNumber) // 证书序列号
                    .apiV3Key(apiV3Key) // APIV3密匙
                    .build();

            // 初始化 NotificationParser
            NotificationParser parser = new NotificationParser(config);
            // 解析为Transaction对象（解密数据）
            try {
                transaction = parser.parse(requestParam, Transaction.class);

                response.setStatus(200);
                jsonObject.put("code", "SUCCESS");
                jsonObject.put("message", "成功");
                result.setSuccess(true);

                log.info(JSON.toJSONString(transaction));
            } catch (Exception e) {
                e.printStackTrace();
                response.setStatus(401);
                jsonObject.put("code", "ERROR");
                jsonObject.put("message", "失败");
                result.setSuccess(false);
            }
        } catch (Exception e) {
            e.printStackTrace();
            response.setStatus(500);
            jsonObject.put("code", "ERROR");
            jsonObject.put("message", "失败");
            result.setSuccess(false);
        }
        result.setResult(jsonObject.toString());
        result.setTransaction(transaction);


        return result;
    }

    @Data
    public static class WxPayResult {
        private Transaction transaction;
        private String result;
        private boolean isSuccess;//是否成功
    }


    public WxMiniProgramPayClientReqeust getWxMiniProgramPayClientReqeust(String prepay_id) throws Exception {
        String appid = wxProperties.getMinAppid();
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = IdUtil.nanoId();
        String packageStr = "prepay_id=" + prepay_id;
        WxMiniProgramPayClientReqeust request = new WxMiniProgramPayClientReqeust();
        request.setTimeStamp(timestamp);
        request.setNonceStr(nonceStr);
        request.setPackageStr(packageStr);

        //新建一个StringBuilder对象，并将上述参数拼接在一起，用\n作为分隔符
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder
                .append(appid).append("\n")
                .append(timestamp).append("\n")
                .append(nonceStr).append("\n")
                .append(packageStr).append("\n");
        String sign = generateSignature(stringBuilder.toString(), privateKeyPath);
        request.setPaySign(sign);

        return request;

    }


    /**
     * 获取支付签名认证串
     * 该方法用于生成微信支付所需的签名认证串，以确保支付请求的安全性
     * 它需要从WxPayUtil类中获取商户ID、私钥路径、商户证书序列号、API v3密钥和应用ID等配置信息
     * 然后构造请求参数，并生成签名，最终返回签名认证串
     *
     * @return 生成的支付签名认证串，如果生成失败则返回null
     */
    public String getPayAuth(String jsonBody) {
        try {
            //构造微信小程序支付下单的签名认证串
            //        HTTP请求方法\n
            //        URL\n
            //        请求时间戳\n
            //        请求随机串\n
            //        请求报文主体\n
            String method = "POST";
            String url = "/v3/pay/transactions/jsapi";
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
            String nonce = IdUtil.fastSimpleUUID();
            //用stringBuilder将上面的参数拼接到一起，每个参数之间用\n分隔
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(method)
                    .append("\n")
                    .append(url)
                    .append("\n")
                    .append(timestamp)
                    .append("\n")
                    .append(nonce)
                    .append("\n")
                    .append(jsonBody)
                    .append("\n");
            // 4. 返回签名认证串
            return generateSignature(stringBuilder.toString(), privateKeyPath);
        } catch (Exception e) {
            log.error("生成支付签名认证串失败", e);
            return null;
        }
    }


    // 生成签名的方法
    private String generateSignature(String parms, String privateKeyPath) throws Exception {
        // 读取私钥
        PrivateKey privateKey = readPrivateKey(privateKeyPath);
        // 签名
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(parms.getBytes(StandardCharsets.UTF_8));
        byte[] signed = sign.sign();

        // 将签名转换为Base64字符串
        return Base64.getEncoder().encodeToString(signed);
    }

    // 读取私钥的方法
    private PrivateKey readPrivateKey(String privateKeyPath) throws Exception {
        FileReader fileReader = new FileReader(privateKeyPath);
        PemReader pemReader = new PemReader(fileReader);
        PemObject pemObject = pemReader.readPemObject();
        byte[] content = pemObject.getContent();

        if (pemObject.getType().equals("PRIVATE KEY")) {
            // PKCS#8 format
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(content);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(spec);
        } else if (pemObject.getType().equals("RSA PRIVATE KEY")) {
            // Traditional OpenSSL format
            PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(content);
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
            return converter.getPrivateKey(privateKeyInfo);
        } else {
            throw new IllegalArgumentException("Unsupported PEM type: " + pemObject.getType());
        }
    }


}
